package com.xuecheng.message.sdk.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.message.sdk.constant.MqMessageStatusConstant;
import com.xuecheng.message.sdk.mapper.MqMessageHistoryMapper;
import com.xuecheng.message.sdk.mapper.MqMessageMapper;
import com.xuecheng.message.sdk.service.IMqMessageService;
import com.xuecheng.message.sdk.model.po.MqMessage;
import com.xuecheng.message.sdk.model.po.MqMessageHistory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author itcast
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MqMessageServiceImpl extends ServiceImpl<MqMessageMapper, MqMessage> implements IMqMessageService {

    /**
     * 消息dao
     */
    private final MqMessageMapper mqMessageMapper;

    /**
     * 历史消息dao
     */
    private final MqMessageHistoryMapper mqMessageHistoryMapper;

    /**
     * 根据分片获取要处理的消息列表
     *
     * @param shardIndex  分片索引
     * @param shardTotal  分片总数
     * @param messageType 消息类型
     * @param count       每次获取消息的数量
     * @return List<MqMessage> 消息列表
     */
    @Override
    public List<MqMessage> getMessageList(int shardIndex, int shardTotal, String messageType, int count) {
        return mqMessageMapper.selectListByShardIndex(shardTotal, shardIndex, messageType, count, MqMessageStatusConstant.INIT, MqMessageStatusConstant.MAX_RETRY_COUNT);
    }

    /**
     * 添加消息
     *
     * @param messageType  消息类型
     * @param businessKey1 关联业务信息1
     * @param businessKey2 关联业务信息2
     * @param businessKey3 关联业务信息3
     * @return 消息
     */
    @Override
    public MqMessage addMessage(String messageType, String businessKey1, String businessKey2, String businessKey3) {
        MqMessage mqMessage = MqMessage.builder()
                .messageType(messageType)
                .businessKey1(businessKey1)
                .businessKey2(businessKey2)
                .businessKey3(businessKey3)
                .build();
        return mqMessageMapper.insert(mqMessage) > 0 ? mqMessage : null;
    }

    /**
     * 完成消息阶段1
     *
     * @param id 消息id
     * @return 完成任务：1，未完成任务：0
     */
    @Override
    public void completedStageOne(long id) {
        lambdaUpdate()
                .set(MqMessage::getStageState1, MqMessageStatusConstant.SUCCESS)
                .eq(MqMessage::getId, id)
                .update();
    }

    /**
     * 完成消息阶段2
     *
     * @param id 消息id
     * @return 完成任务：1，未完成任务：0
     */
    @Override
    public void completedStageTwo(long id) {
        lambdaUpdate()
                .set(MqMessage::getStageState2, MqMessageStatusConstant.SUCCESS)
                .eq(MqMessage::getId, id)
                .update();
    }

    /**
     * 完成消息阶段3
     *
     * @param id 消息id
     * @return 完成任务：1，未完成任务：0
     */
    @Override
    public void completedStageThree(long id) {
        lambdaUpdate()
                .set(MqMessage::getStageState3, MqMessageStatusConstant.SUCCESS)
                .eq(MqMessage::getId, id)
                .update();
    }

    /**
     * 完成消息阶段4
     *
     * @param id 消息id
     * @return 完成任务：1，未完成任务：0
     */
    @Override
    public void completedStageFour(long id) {
        lambdaUpdate()
                .set(MqMessage::getStageState4, MqMessageStatusConstant.SUCCESS)
                .eq(MqMessage::getId, id)
                .update();
    }

    /**
     * 获取消息阶段1
     *
     * @param id 消息id
     * @return 阶段一完成：1，未完成：0
     */
    @Override
    public int getStageOne(long id) {
        return Integer.parseInt(getById(id).getStageState1());
    }

    /**
     * 获取消息阶段2
     *
     * @param id 消息id
     * @return 阶段二完成：1，未完成：0
     */
    @Override
    public int getStageTwo(long id) {
        return Integer.parseInt(getById(id).getStageState2());
    }

    /**
     * 获取消息阶段3
     *
     * @param id 消息id
     * @return 阶段三完成：1，未完成：0
     */
    @Override
    public int getStageThree(long id) {
        return Integer.parseInt(getById(id).getStageState3());
    }

    /**
     * 获取消息阶段4
     *
     * @param id 消息id
     * @return 阶段四完成：1，未完成：0
     */
    @Override
    public int getStageFour(long id) {
        return Integer.parseInt(getById(id).getStageState4());
    }

    /**
     * 消息处理成功
     *
     * @param id 消息id
     */
    @Override
    @Transactional
    public void success(Long id) {
        // 1. 更新消息表(假装更)
        MqMessage mqMessage = getById(id);
        mqMessage.setExecuteNum(mqMessage.getExecuteNum() + 1)
                .setState(MqMessageStatusConstant.SUCCESS)
                .setReturnSuccessDate(LocalDateTime.now())
                .setExecuteDate(LocalDateTime.now())
                .setReturnFailureMsg(null)
                .setReturnFailureDate(null);
        // 2. 添加到历史表
        MqMessageHistory mqMessageHistory = BeanUtil.copyProperties(mqMessage, MqMessageHistory.class);
        mqMessageHistoryMapper.insert(mqMessageHistory);
        // 3. 删除消息表
        removeById(id);
    }

    /**
     * 消息处理失败
     *
     * @param id         消息id
     * @param failureMsg 失败原因
     */
    @Override
    @Transactional
    public int fail(long id, String failureMsg) {
        // 1. 更新消息表
        MqMessage mqMessage = getById(id);
        mqMessage.setExecuteNum(mqMessage.getExecuteNum() + 1);
        mqMessage.setExecuteNum(mqMessage.getExecuteNum())
                .setReturnFailureDate(LocalDateTime.now())
                .setReturnFailureMsg(failureMsg)
                .setExecuteDate(LocalDateTime.now());
        updateById(mqMessage);
        // 2. 如果失败次数大于等于最大失败次数，则更新历史表，删除消息表
        if (mqMessage.getExecuteNum() >= MqMessageStatusConstant.MAX_RETRY_COUNT){
            MqMessageHistory mqMessageHistory = BeanUtil.copyProperties(mqMessage, MqMessageHistory.class);
            mqMessageHistoryMapper.insert(mqMessageHistory);
            mqMessageMapper.deleteById(id);
        }
        // 3. 返回执行次数
        return mqMessage.getExecuteNum();
    }
}
